/**
 * GameState class contains all game data, entities and their positions etc, and facilitates their creation, destruction, and management. It does (should) not
 * do any simulation or gameplay related work like setting positions or garrisons.
 *
 * Any requests for entities should return a pointer or container of pointers to the entities and not their ID's unless explicitly stated in the function title
 */

#ifndef MAPINFO_H
#define MAPINFO_H

#include <iostream>
#include <map>
#include <math.h>
#include <memory>
#include <queue>
#include <set>
#include <string>
#include <vector>

#include "utility.h"

#include "primitives/graph.h"

class Army;
class Building;
class City;
class Entity;
class Improvement;
class Player;
class River;
class Road;
class Settler;
class Tile;
class Unit;

class GameState{
    public:
        GameState();
        void Initialise();
        void CreateMap();
        void CreatePlayer(bool ai);

        int MapWidth(){ return mapWidth; }
        int MapHeight(){ return mapHeight; }
        int MapSize(){ return mapWidth*mapHeight; }

        void SetMapWidth(int w){ mapWidth = w; }
        void SetMapHeight(int h){ mapHeight = h; }
        void SetMap(int width, int height, std::vector<Tile*> newTiles);

        int xComponent(int index){ return index%mapWidth; }
        int yComponent(int index){ return index/mapWidth; }
        int Index(int x, int y){ return x+mapWidth*y; }

        bool IsValidIndex(int index){ if(index>=MapSize() || index<0) return false; else return true; }

        float Distance(float a1, float a2, float b1, float b2){ return sqrt((a1-b1)*(a1-b1) + (a2-b2)*(a2-b2)); }
        float Distance(int i1, int i2){ return Distance(xComponent(i1), yComponent(i1), xComponent(i2), yComponent(i2)); }

        Entity* GetEntity(int id);
        std::vector<Entity*> GetAllEntities();
        int GetSelectedEntityID(){ return selected_entity; }
        Entity* GetSelectedEntity(){ return GetEntity(selected_entity); }
        int GetActiveEntityID(){ return active_entity; }
        Entity* GetActiveEntity(){ return GetEntity(active_entity); }
        void SetActiveEntity(int id){ active_entity = id; }
        void SelectEntity(int id);
        void DeselectEntity();

        Tile* GetTileByIndex(int index);
        Tile* GetTileByID(int id);
        std::vector<Tile*> GetAllTiles();
        std::vector<int> GetTileIndicesControlledByPlayer(int player_id);
        std::set<int> GetTileIndicesWithinDistance(int position, float distance);
        std::vector<Tile*> GetTilesByID(std::set<int> ids);

        City* CreateCity(int index, int owner);
        City* GetCity(int id);
        std::vector<City*> GetAllCities(){ return Utility::UniqueToRaw(cities); }

        Building* CreateBuilding(std::string name);
        Building* GetBuilding(int id);

        Improvement* CreateImprovement();
        Improvement* GetImprovement(int id);
        Improvement* GetImprovementByIndex(int index);
        Improvement* GetImprovementContainingBuilding(int id);
        std::vector<Improvement*> GetAllImprovements(){ return Utility::UniqueToRaw(improvements); }
        bool DeleteImprovement(int id);

        Army* CreateArmy();
        Army* GetArmy(int id);
        Army* GetArmyByIndex(int index);
        std::vector<Army*> GetAllArmies();
        void DeleteArmy(int id);

        Unit* CreateUnit(std::string name);
        Unit* GetUnit(int id);

        void AddPathToRoadNetwork(std::vector<int> path, int weight = 1);
        Graph & GetRoads(){ return roads; }

        void AddPathToRiverNetwork(std::vector<int> path);
        Graph & GetRivers(){ return rivers; }

        std::vector<Player*> GetPlayers(){ return Utility::UniqueToRaw(players); }
        Player* GetPlayer(int player){ return players.at(player).get(); }
        Player* GetActivePlayer(){ return players.at(active_player).get(); }
        int GetActivePlayerID(){ return active_player; }
        void SwitchToNextPlayer();

        void SetHoverImprovementID(int id){ hover_improvement = id; }
        int GetHoverImprovementID(){ return hover_improvement; }
        Improvement* GetHoverImprovement(){ return GetImprovement(hover_improvement); }

        int NumPlayers(){ return players.size(); }
        int NumTiles(){ return tiles.size(); }
        int NumArmies(){ return armies.size(); }
        int NumImprovements(){ return improvements.size(); }

        void SetModifierKey(std::string name, bool down);
        bool GetModifierKey(std::string name);

        void ClearSelectedUnits(){ selected_units.clear(); }
        void ToggleUnitInSelectedUnits(int unit);
        void RemoveUnitFromSelectedUnits(int unit);
        int NumSelectedUnits(){ return selected_units.size(); }

        void MarkForDeletion(int id){ unitsToDelete.push_back(id); }
        void DeleteMarkedUnits();
        bool DeleteUnit(int id);

        bool DeleteBuilding(int id);

        void DeleteMarkedEntities();

        bool HasInfluenceChanged(){ return influenceChanged; }
        void ResetInfluenceChanged(){ influenceChanged = false; }

        void IncrementTurn(){ turn++; std::cout<<"Turn "<<turn<<" has begun.\n"; }
        int GetTurn(){ return turn; }

        void AddTileToProvince(int province_id, int tile_index);
        int GetProvinceIDContainingTile(int tile_index);
        std::set<int> GetProvince(int id){ return provinces.at(id); }
        std::map<int, std::set<int>> GetAllProvinces(){ return provinces; }
        std::set<int> GetTileIndicesBorderingProvince(int id);

    private:
        void DeleteEntityByID(int id);

        int mapWidth;
        int mapHeight;

        int turn;
        int active_player;
        int selected_entity;
        int active_entity;

        bool influenceChanged;

        std::vector<std::unique_ptr<Player>> players;

        std::vector<int> guiSelections;

        std::vector<std::unique_ptr<Tile>> tiles;

        std::map<int, std::set<int>> provinces;

        Graph rivers;
        Graph roads;

        std::vector<std::unique_ptr<Building>> buildings;
        std::vector<std::unique_ptr<Improvement>> improvements;
        std::vector<std::unique_ptr<City>> cities;
        int hover_improvement;
        std::vector<std::unique_ptr<Unit>> units;
        std::vector<std::unique_ptr<Army>> armies;

        std::map<std::string, bool> modifierKeys;

        std::vector<int> selected_units;

        std::vector<int> unitsToDelete;
        std::queue<std::unique_ptr<Entity>> entitiesToDelete;
};

extern GameState* gamestate;

#endif
